package com.jbp.service;

import com.alibaba.fastjson.JSON;
import com.jbp.domin.LuckPool;
import com.jbp.domin.LuckUser;
import com.jbp.exception.RRException;
import com.jbp.model.Gift;
import com.jbp.model.GiftLot;
import com.jbp.redis.RedisConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;

/**
 * 飞马 2块公池 1：10
 */
@Service
public class LuckyUtil101 {
    @Autowired
    private RedisConfigService redisConfigService;
    @Autowired
    private PropertyService propertyService;
    //每次抽消耗奖金币
    public static final int price = 20;

    @Autowired
    private LuckyUtil luckyUtil;
    /**
     * patch 补礼物  0表示不补
     * */
    public List<Gift> luck(Integer userId, int times, List<GiftLot> list, int patch, Integer roomId) {
        //奖池礼物数量
        int poolSize = Integer.parseInt(propertyService.getV("pool_size"));
        int minPoolSize = Integer.parseInt(propertyService.getV("min_pool_size"));
        int maxGift = Integer.parseInt(propertyService.getV("maxGift"));
        double jcBeishu = Double.parseDouble(propertyService.getV("pool_times"));

        String keyUmap = "haoya:luck:"+price+":umap:"+userId;

        LuckUser luckUser = luckyUtil.getLuckUser(userId, price);
        int pay = luckUser.getPay();
        int income = luckUser.getIncome();

        double userBaseRate = luckyUtil.getBaseRate(roomId, userId, pay);

        //判断奖池
        int type = 1;
        int biggest = 20000;
        double basePoolRate = 1;

        LuckPool luckPool = luckyUtil.getLuckPool(price, "A", roomId, userId);

        double winRate = luckPool.getWinRate();

        double patchRate = winRate<1.17 ? 1.17-winRate : 0;

        Node nodeTree = getNodeTree(type);

        int allIncome = luckPool.getAllIncome();
        int allPay = luckPool.getAllPay();
        int pool = luckPool.getPool();
        int giftCount = luckPool.getGiftCount();

        //用户积分树
        String ujson = (String) redisConfigService.get(keyUmap);
        Map<String, Integer> userNMap = (Map<String, Integer>) JSON.parse(ujson);
        if (userNMap==null) {
            userNMap = new LinkedHashMap<>();
        }

        Map<String, Integer> allNMap = luckPool.getPoolMap();
        //生成奖池
        if (giftCount<=minPoolSize) {
            giftCount += poolSize;
            pool += poolSize * winRate * price;
            if (pool>giftCount*1.19*price) {
                pool = (int) (giftCount*1.19*price);
            }
            System.out.println("生成奖池："+pool);
        }

        List<Gift> result = new ArrayList<>();
        for (int i = 0; i < times; i++) {
            giftCount --;
            pay += price;
            allPay += price;

            //用户最大奖限制
            biggest = Math.min(biggest, (int) (pool/jcBeishu));
            int min = 1;
            int luck;
            if (patch==0) {
                biggest = Math.min(biggest, maxGift);
                biggest = Math.max(biggest, 6666);
                biggest = Math.min(biggest, 131400);

                int protectPrice = protectPrice(pay, income);
                if (protectPrice>0) {
                    biggest = Math.min(biggest, 50000);
                    min = protectPrice;
                }

                min=Math.min(min, 20000);
                min = Math.min(min, biggest);
                min = Math.min(min, 5200);

                double poolRate = LuckyUtil.getPoolRate(pool, giftCount, winRate, price);
                poolRate = Math.min(poolRate, 1.2d);
                luck = LuckyUtil.nodeLuck(nodeTree, poolRate, min, biggest, userNMap, allNMap, basePoolRate*userBaseRate);
            } else {
                luck = patch;
            }

            //将奖品转化为礼物
            Gift gift = null;
            for (GiftLot giftLot : list) {
                if (giftLot.getGift().getCoin() == luck) {
                    gift = giftLot.getGift();
                    break;
                }
            }

            if (gift==null) {
                throw new RRException("网络错误");
            }

            income += gift.getCoin();
            allIncome += gift.getCoin();
            if (patch==0) {
                pool -= gift.getCoin();
            }
            result.add(gift);
        }

        pool = Math.min(pool, (int) (giftCount*1.18*price+30000));

        redisConfigService.set(keyUmap, JSON.toJSONString(userNMap));

        luckyUtil.addPatch((int) (poolSize * patchRate * price));

        luckUser.setPay(pay);
        luckUser.setIncome(income);
        luckyUtil.updateLuckUser(luckUser);

        luckPool.setAllIncome(allIncome);
        luckPool.setAllPay(allPay);
        luckPool.setPool(pool);
        luckPool.setGiftCount(giftCount);
        luckPool.setPoolMap(allNMap);
        luckyUtil.updateLuckPool(luckPool);

        return result;
    }

    public int protectPrice(int totalPay, int totalIncome) {
        double protectedRate = 0;
        if (totalPay>500000) {
            protectedRate = 1.02;
        } else if (totalPay>400000) {
            protectedRate = 0.99;
        } else if (totalPay > 350000) {
            protectedRate = 0.985;
        } else if (totalPay > 200000) {
            protectedRate = 0.96;
        } else if (totalPay>145000) {
            protectedRate = 0.94;
        } else if (totalPay>95000) {
            protectedRate = 0.85;
        } else if (totalPay>50000) {
            protectedRate = 0.6;
        }

        int min = 0;
        if (protectedRate>0 && totalPay*protectedRate > totalIncome) {
            min = (int) (totalPay*protectedRate-totalIncome);
            if (totalPay>500000) {
                min = Math.max(min, 5000);
            } else if (totalPay>250000) {
                min = Math.max(min, 1000);
            } else if (totalPay>160000) {
                min = Math.max(min, 500);
            }
        }

        return min;
    }

    Pool poolA = new Pool();

    @SuppressWarnings("AliControlFlowStatementWithoutBraces")
    void luckTest(UserLuck userLuck, Map <Integer, Integer> out) {
        //总爆率
        double winRate;

        Pool pl;
        Node nodeTree;
        int type;

        //用户最大奖限制
        int min = 1;

        int biggest = 70000;
        biggest = Math.max(biggest, 6000);

        pl = poolA;
        type =1;
        winRate = 1.18;
        nodeTree = getNodeTree(type);

        //生成奖池
        if (pl.giftCount<10000) {
            pl.giftCount += pl.giftSize;
            pl.pool += pl.giftSize * winRate * price;
        }

        pl.giftCount --;
        userLuck.pay += price;
        userLuck.totalPay += price;
        userLuck.currPay += price;
        pl.allPay += price;

        biggest = Math.min(biggest, pl.pool/5);


        double protectedRate = 0;
        int totalPay = userLuck.totalPay;
        int totalIncome = userLuck.totalIncome;
        if (totalPay>500000) {
            protectedRate = 1.02;
        } else if (totalPay>280000) {
            protectedRate = 0.99;
        } else if (totalPay > 250000) {
            protectedRate = 0.985;
        } else if (totalPay>145000) {
            protectedRate = 0.97;
        } else if (totalPay>95000) {
            protectedRate = 0.95;
        } else if (totalPay>50000) {
            protectedRate = 0.6;
        } else if (totalPay>8000) {
            protectedRate = 0.55;
        }
        if (protectedRate>0 && totalPay*protectedRate > totalIncome) {
            min = (int) (totalPay*protectedRate-totalIncome);
            biggest = Math.min(biggest, 50000);
            if (totalPay>500000) {
                min = Math.max(min, 5000);
            } else if (totalPay>250000) {
                min = Math.max(min, 1000);
            } else if (totalPay>160000) {
                min = Math.max(min, 500);
            }  else {
                min = Math.max(min, 100);
            }
        }

        min=Math.min(min, 20000);

        min = Math.min(min, biggest);

        double poolRate = LuckyUtil.getPoolRate(pl.pool, pl.giftCount, winRate, price);
        Map<String, Integer> nMap = getUserNMap(userLuck.id);
        Map<String, Integer> allMap = getPoolNMap(type);
        int luck = LuckyUtil.nodeLuck(nodeTree, poolRate, min, biggest, nMap, allMap, 1);

        userLuck.income += luck;
        userLuck.totalIncome += luck;
        userLuck.currIncome += luck;
        pl.allIncome += luck;
        pl.pool -= luck;

        if (out.containsKey(luck)) {
            out.put(luck, out.get(luck)+1);
        } else {
            out.put(luck, 1);
        }
    }


    /**
     * A奖池 投入1w钻以内
     */
    private static Map <Integer, Integer> getGiftCoinfigA() {
        Map<Integer, Integer> map = new LinkedHashMap<>();
        map.put(1, 33651);
        map.put(10, 8000);
        map.put(30, 2800);
        map.put(52, 550);
        map.put(199, 250);
        map.put(520, 350);
        map.put(999, 140);
        map.put(1111, 80);
        map.put(1999, 55);
        map.put(3344, 40);
        map.put(5200, 20);
        map.put(8888, 2);
        map.put(11111, 1);
        return map;
    }

    /**
     1. 存储用户每个礼物对应的N值 默认为1 未抽中+1 抽中置为1
     2. prd算法 P(实际概率)=C(基础爆率)*N(抽奖次数)
     */
    private static Map<Integer, Map<Integer, Integer>> userNMap = new HashMap<>();

    private static Node getNodeTree(Map<String, Integer> map){
        Node h2 = new Node("h2", new Node(52000), new Node(66666), map);
        Node h1 = new Node("h1", new Node(11111), new Node(33333), map);
        Node g2 = new Node("g2", h1, h2, map);
        Node g1 = new Node("g1", new Node(5200), new Node(8888), map);
        Node f2 = new Node("f2", g1, g2, map);
        Node f1 = new Node("f1", new Node(1999), new Node(3344), map);
        Node e1 = new Node("e1", new Node(999), new Node(1111), map);
        Node e2 = new Node("e2", f1, f2, map);
        Node d1 = new Node("d1", new Node(199), new Node(520), map);
        Node d2 = new Node("d2", e1, e2, map);
        Node c1 = new Node("c1", new Node(30), new Node(52), map);
        Node c2 = new Node("c2", d1, d2, map);
        Node b1 = new Node("b1", new Node(1), new Node(10), map);
        Node b2 = new Node("b2", c1, c2, map);
        return new Node("a", b1, b2, map);
    }

    private static Map <String, Integer> getCMap1() {
        Map<String, Integer> map = new LinkedHashMap<>();
        map.put("a", 128);
        map.put("b1", 511);
        map.put("b2", 653);
        map.put("c1", 381);
        map.put("c2", 1638);
        map.put("d1", 3985);
        map.put("d2", 1563);
        map.put("e1", 1682);
        map.put("e2", 529);
        map.put("f1", 2198);
        map.put("f2", 29);
        map.put("g1", 122);
        map.put("g2", 0);
        map.put("h1", 0);
        map.put("h2", 0);
        return map;
    }
    static Node node1;

    private static Node getNodeTree(int type){
        if(node1==null) {
            node1 = getNodeTree(getCMap1());
        }
        return node1;
    }

    private static Map<Integer, Map<String, Integer>> userNMap1 = new HashMap<>();
    private static Map<Integer, Map<String, Integer>> poolNMap = new HashMap<>();

    /**奖池总积分树*/
    static Map<String, Integer> getPoolNMap(int type) {
        Map<String, Integer> nMap = poolNMap.get(type);
        if (nMap==null) {
            nMap = new LinkedHashMap<>();
            poolNMap.put(type, nMap);
        }
        return nMap;
    }

    /**用户积分树*/
    static Map<String, Integer> getUserNMap(int id) {
        Map<String, Integer> nMap = userNMap1.get(id);
        if (nMap==null) {
            nMap = new LinkedHashMap<>();
            userNMap1.put(id, nMap);
        }
        return nMap;
    }

    /**
     * 抽中礼物 跟新N值
     * @Param luck 当次中间礼物
     * */
    private static void updateLuck1(Integer userId, String... names) {
        Map<String, Integer> nMap = userNMap1.get(userId);
        if (nMap==null) {
            nMap = new LinkedHashMap<>();
            for (String k : getCMap1().keySet()) {
                nMap.put(k, 1);
            }
        }

        List list = CollectionUtils.arrayToList(names);
        for (String  k: nMap.keySet()) {
            if (list.contains(k)) {
                nMap.put(k, 1);
            } else {
                nMap.put(k, nMap.get(k)+1);
            }
        }

        userNMap1.put(userId, nMap);
    }

    static void testLuck2(Map <String, Integer> map){

        Map<Integer, Integer> out = new HashMap<>();
        for (int j = 0; j < 1; j++) {
            int id = 1;
            for (int i = 0; i < 1000000; i++) {
                Map<String, Integer> nMap = getUserNMap(i);
                Map<String, Integer> allMap = getPoolNMap(1);
                int luck = LuckyUtil.nodeLuck(getNodeTree(map), 1, 1, 2000000, nMap, allMap, 1);

                if (out.containsKey(luck)) {
                    out.put(luck, out.get(luck) + 1);
                } else {
                    out.put(luck, 1);
                }
            }
        }
        LuckyUtil.printConfig(out, price);
    }

    public static void main(String[] args) {
//        System.out.println("-------A-------");
//        printConfig(getGiftCoinfigA());
//        System.out.println("-------B-------");
//        printConfig(getGiftCoinfigB());
//        System.out.println("--------C------");
//        printConfig(getGiftCoinfigC());
//        System.out.println("-------D-------");
//        printConfig(getGiftCoinfigD());

        /*printConfig(getGiftCoinfigA());
        System.out.println("--------------");
        testLuck2(getCMap1());
        System.out.println("--------------");
        calPercent(getNodeTree(getCMap1()), getGiftCoinfigA());*/

        LuckyUtil101 luckyUtil = new LuckyUtil101();
        Map <Integer, Integer> result = new LinkedHashMap<>();
        List<UserLuck> userLucks = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            UserLuck user = new UserLuck();
            user.id=i;
            while (true) {
                luckyUtil.luckTest(user, result);
                if (user.totalPay>=10000 || (user.totalIncome*0.85d-user.totalPay>0)) {
//                if (user.totalPay>=400000) {
                    double rate  = BigDecimal.valueOf(user.totalIncome).divide(BigDecimal.valueOf(user.totalPay), 4, BigDecimal.ROUND_HALF_UP).doubleValue();
                    System.out.println(i+": 投入："+user.totalPay+" 产出:"+user.totalIncome +"  爆率:"+rate+ " 实际盈利："+(int)(user.totalIncome*0.85d-user.totalPay));
                    break;
                }
            }
            userLucks.add(user);
        }

        LuckyUtil.printConfig(result, price);

        LuckyUtil.tjUser(userLucks);

        System.out.println("------------系统爆率------------");
        System.out.println("A 系统投入:"+luckyUtil.poolA.allPay+" 产出:"+luckyUtil.poolA.allIncome+" 总爆率:"+ ((double)luckyUtil.poolA.allIncome/luckyUtil.poolA.allPay)+" 奖池剩余："+luckyUtil.poolA.giftCount+" 剩余爆率:"+((double)luckyUtil.poolA.pool/price/luckyUtil.poolA.giftCount));
    }
}

